#include "region.hpp"
#include "village.hpp"

#include "nlohmann/json.hpp"
#include "yaml-cpp/yaml.h"



using json = nlohmann::json;

Region::Region() = default;

Region::Region(string path)
{
    // clear the vector
    villages.clear();

    // load the villages from json or yaml file
    if(path.substr(path.find_last_of(".") + 1) == "json")
    {
        // handle json file
        std::cout << "JSON path: " << path << std::endl;
        load_json(path);

        // // write prettified JSON to another file
        // std::ofstream o("pretty.json");
        // o << std::setw(4) << j << std::endl;
    }
    else
    {
        // handle yaml file
        std::cout << "YAML path: " << path << std::endl;
        load_yaml(path);
    }

}

void Region::load_yaml(string yaml_path)
{
    // handle yaml file
    YAML::Node config = YAML::LoadFile(yaml_path);
    YAML::Node v_list = config["villages"];

    // range-based for
    // for (YAML::const_iterator it=v_list.begin();it!=v_list.end();++it)
    // {
    //     std::cout << *it["id"].as<int>() << "\n";
    // }
    for (std::size_t i=0; i<v_list.size(); i++)
    {
        std::cout << "ID: " << v_list[i]["id"] << "  "
                  << "x:"   << v_list[i]["x"]  << "  "
                  << "y:"   << v_list[i]["y"] << std::endl;

        Village v(v_list[i]["id"].as<int>(),
                  v_list[i]["x"].as<double>(),
                  v_list[i]["y"].as<double>());
        villages.push_back(v);
    }
}

void Region::load_json(string json_path)
{
    // handle json file
    std::ifstream json_stream(json_path);
    json j;
    json_stream >> j;

    // std::cout << j.is_object() << std::endl;

    // range-based for
    for (auto& element : j["villages"])
    {
        // add each item to the vector
        std::cout << "ID: " << element["id"] << "  "
                  << "x:"   << element["x"]  << "  "
                  << "y:"   << element["y"] << std::endl;

        Village v(element["id"], element["x"], element["y"]);
        villages.push_back(v);
    }
}


vector<Village> Region::get_region()
{
    return villages;
}

Village* Region::get_village(int id)
{
    vector<Village>::iterator it = find_if(
        villages.begin(),
        villages.end(),
        [id] (const Village& v) { return v.get_id() == id; });

    if (it != villages.end())
    {
        return &(*it);
    }
    else
    {
        return nullptr;
    }
}

bool Region::set_village(int id, double x, double y)
{
    Village* pVillage = get_village(id);

    if (pVillage != nullptr)
    {
        pVillage->set_x(x);
        pVillage->set_y(y);

        return true;
    }
    else
    {
        return false;
    }
}

bool Region::add_village(Village v)
{
    Village* pVillage = get_village(v.get_id());

    if (pVillage != nullptr) // id existed, report add_village error
    {
        return false;
    }
    else
    {
        villages.push_back(v);

        return true;
    }
}


bool Region::add_village(int id, double x, double y)
{
    Village* pVillage = get_village(id);

    if (pVillage != nullptr) // id existed, report add_village error
    {
        return false;
    }
    else
    {
        Village v(id, x, y);

        villages.push_back(v);

        return true;
    }
}

bool Region::del_village(int id)
{
    vector<Village>::iterator it = find_if(
        villages.begin(),
        villages.end(),
        [id] (const Village& v) { return v.get_id() == id; });

    if (it != villages.end())
    {
        villages.erase(it);

        return true;
    }
    else
    {
        return false;
    }
}

// MatrixXd Region::region2dist()
// {
//     int N = villages.size();

//     MatrixXd m(N, N);
//     for (size_t i = 0; i < N; i++)
//     {
//         for (size_t j = 0; j < N; j++)
//         {
//             m(i, j) = (villages[i].get_x() - villages[j].get_x())*(villages[i].get_x() - villages[j].get_x()) + 
//                       (villages[i].get_y() - villages[j].get_y())*(villages[i].get_y() - villages[j].get_y());
//             m(i, j) = std::sqrt(m(i, j));
//         }
//     }
    
//     // std::cout << m << std::endl;

//     return m;
// }

// vector<VillageGraph> Region::min_region_dist()
// {
//     MatrixXd m = region2dist();

//     std::map<int, double> node_cost;
//     for (size_t i = 0; i < villages.size(); i++)
//     {
//         node_cost[villages[i].get_id()] = std::numeric_limits<double>::infinity();
//     }
    
// }


// unordered_set<Edge, EdgeHash, EdgeEqual> primMST(Graph graph){
//     // 装边的最小堆
//     auto cmp = [](const Edge& e1, const Edge& e2){
//         return e1.weight > e2.weight;
//     };
//     priority_queue<Edge, vector<Edge>, decltype(cmp)> smallQueue(cmp);
//     // 判断节点是否访问过
//     unordered_set<Node, NodeHash, NodeEqual> node_set;
//     unordered_set<Edge, EdgeHash, EdgeEqual> result;
//     for(auto ite: graph.nodes){
//         if(node_set.find(*ite.second) == node_set.end()){
//             // 如果没有访问,将其标记为访问过，并把它对应的边放入最小堆
//             node_set.insert(*ite.second);
//             for(Edge* edge: ite.second->edges){
//                 smallQueue.push(*edge);
//             }
//             // 在当前这个图中寻找最小生成树
//             while(!smallQueue.empty()){
//                 // 从堆中取出一个最小权重边，并取出对应节点
//                 Edge help_edge = smallQueue.top();
//                 smallQueue.pop();

//                 Node edge_to = *(help_edge.to);
//                 // 然后判断这个节点是否被访问过，如果没有则将这个边加入边集
//                 if(node_set.find(edge_to) == node_set.end()){
//                     result.insert(help_edge);
//                     node_set.insert(edge_to);   // 标记edge_to也已经访问过了
//                     for(Edge *newEdge: edge_to.edges){
//                         smallQueue.push(*newEdge);
//                     }
//                 }
//             }
//         }
//     }
//     return result;
// }